package oceanlife.pathfinding;

import java.awt.Component;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.Arrays;

import oceanlife.Agent;
import oceanlife.OLSet;

public class SimpleObstacle {
	private Double borderThickness = Agent.radius;
	private ArrayList<Pnt> pnts;
	private ArrayList<Pnt> pathPnts;
	private ArrayList<Line2> lines = new ArrayList<Line2>();
	private ArrayList<Line2> pathLines = new ArrayList<Line2>();
	private ArrayList<Line2> connectionLines = null;
	
	public SimpleObstacle(Pnt centre, Double radius){
		pnts = new ArrayList<Pnt>();
		pnts.add(new Pnt(centre.coord(0)+radius,centre.coord(1)+radius));
		pnts.add(new Pnt(centre.coord(0)+radius,centre.coord(1)-radius));
		pnts.add(new Pnt(centre.coord(0)-radius,centre.coord(1)-radius));
		pnts.add(new Pnt(centre.coord(0)-radius,centre.coord(1)+radius));
		pntsToLines(pnts,lines);
		createSurroundingPathLines();
	}
	
	public SimpleObstacle(ArrayList<Pnt> pnts) throws Exception{
		this.pnts = pnts;
		pntsToLines(this.pnts,lines);
		createSurroundingPathLines();
	}
	
	public SimpleObstacle(Pnt[] pnts) {
		this.pnts = new ArrayList<Pnt>(Arrays.asList(pnts));
		pntsToLines(this.pnts,lines);
		createSurroundingPathLines();
	}

	private void pntsToLines(ArrayList<Pnt> pnts, ArrayList<Line2> lines){
		lines.clear();
		for(int i = 0; i < pnts.size(); i++){
			int j = i+1;
			if(j == pnts.size()){
				j = 0;
			}
			lines.add(new Line2(pnts.get(i),pnts.get(j)));
		}
	}
	
	public void createSurroundingPathLines(){
		pathPnts = new ArrayList<Pnt>();
		for(Line2 line : lines){
			Pnt translationPnt = new Pnt(borderThickness*Math.sin(line.angle()+Math.PI/2.0),borderThickness*Math.cos(line.angle()+Math.PI/2.0));
			pathPnts.add(line.getStart().add(translationPnt));
			pathPnts.add(line.getEnd().add(translationPnt));
		}
		//this.pnts = pnts;
		pntsToLines(pathPnts,pathLines);
	}
	
	public boolean intersectsWithObstacle(Line2 line){
		for(Line2 obLine : lines){
			if(line.intersectsLine(obLine)){
				return true;
			}
		}
		return false;
	}

	public boolean intersectsWithObstacle(SimpleObstacle so){
		for(Line2 obLine : so.getObstacleEdges()){
			if(this.intersectsWithObstacle(obLine)){
				return true;
			}
		}
		return false;
	}
	
	public Polygon obstacleToPolygon(){
		return toPolygon(pnts);
	}
	
	public Polygon pathToPolygon(){
		return toPolygon(pathPnts);
	}
	
    public Polygon toPolygon(ArrayList<Pnt> pnts){
    	int polygonSize = pnts.size();
    	int[] xpoints = new int[polygonSize];
    	int[] ypoints = new int[polygonSize];
    	int i = 0;
    	for(Pnt pnt : pnts){
    		xpoints[i] = (int) Math.round(pnt.coord(0));
    		ypoints[i] = (int) Math.round(pnt.coord(1));
    		i++;
    	}
    	return new Polygon(xpoints, ypoints, polygonSize);
    }
	
	public ArrayList<Pnt> getObstacleCorners(){
		return pnts;
	}
	
	public ArrayList<Line2> getObstacleEdges(){
		return lines;
	}
	
	public ArrayList<Pnt> getPathCorners(){
		return pathPnts;
	}
	
	public ArrayList<Line2> getPathEdges(){
		return pathLines;
	}
	
	@Override
    public boolean equals (Object other) {
        if (!(other instanceof SimpleObstacle)) return false;
        SimpleObstacle o = (SimpleObstacle) other;
        if(this.pnts.size() != o.pnts.size()){
        	return false;
        }
        for(int i = 0; i < this.pnts.size(); i++){
        	if(!this.pnts.get(i).equals(o.pnts.get(i))){
        		return false;
        	}
        }
        return true;
    }
	
	public Pnt getClosestPoint(Pnt pos) {
		Pnt closestPoint = pnts.get(0);
		Double closestPointDistance = pos.distanceTo(closestPoint);
		for(int i = 0; i < pnts.size();i++){
			int j = i+1;
			if(j == pnts.size()){
				j = 0;
			}
			Line2 polyLine = new Line2(pnts.get(i),pnts.get(j));
			Pnt potClosestPoint = polyLine.closestPoint(pos);
			double potClosestPointDistance = pos.distanceTo(potClosestPoint);
			if(potClosestPointDistance < closestPointDistance){
				closestPoint = potClosestPoint;
				closestPointDistance = potClosestPointDistance;
			}
		}
		return closestPoint;
	}
	
	public static ArrayList<SimpleObstacle> getInitObstacles(){
		ArrayList<SimpleObstacle> inits = new ArrayList<SimpleObstacle>();
		inits.add(new SimpleObstacle(new Pnt(-OLSet.border,-OLSet.border),1.0));
		inits.add(new SimpleObstacle(new Pnt(OLSet.border,-OLSet.border),1.0));
		inits.add(new SimpleObstacle(new Pnt(0,OLSet.border),1.0));
		return inits;
	}

	public boolean pathContains(Pnt pnt) {
		if(this.pathToPolygon().contains(pnt.coord(0),pnt.coord(1))){
			return true;
		}
		return false;
	}

	public boolean obstacleContains(Pnt pnt) {
		if(this.obstacleToPolygon().contains(pnt.coord(0),pnt.coord(1))){
			return true;
		}
		return false;
	}
	
	//returns shortest line connecting two obstacles. null if intersecting edges.
	public Line2 getShortestLineTo(SimpleObstacle other){
		Line2 shortestLine = new Line2(new Pnt(Double.MAX_VALUE,Double.MAX_VALUE),new Pnt(-Double.MAX_VALUE,-Double.MAX_VALUE));
		for(Line2 thisLine : this.lines){
			for(Line2 otherLine : other.lines){
				Line2 potShortestLine = thisLine.getShortestLineTo(otherLine);
				if (potShortestLine == null){
					return null;
				} else {
					if(potShortestLine.length() < shortestLine.length()){
						shortestLine = potShortestLine;
					}
				}
			}
		}
		return shortestLine;
	}

	public static void main(String[] argc){
		ArrayList<Pnt> pnts = new ArrayList<Pnt>();
		pnts.add(new Pnt(0.0,0.0));
		pnts.add(new Pnt(9.0,0.0));
		pnts.add(new Pnt(11.0,0.0));
		pnts.add(new Pnt(5.0,5.0));
		pnts.add(new Pnt(20.0,20.0));
		pnts.add(new Pnt(11.0,11.0));
		pnts.add(new Pnt(0.0,20.0));
		pnts.add(new Pnt(0.0,11.0));
		SimpleObstacle so = new SimpleObstacle(new Pnt(0,0),10.0);
		System.out.println("obs");
		for(Line2 line : so.getObstacleEdges()){
			System.out.println(line);
		}
		System.out.println("path");
		for(Line2 line : so.getPathEdges()){
			System.out.println(line);
		}
		for(Pnt pnt : pnts){
			if(so.pathContains(pnt)){
				System.out.println(pnt);
			}
		}
	}

	public ArrayList<Line2> getShortestLinesToObstacles(ArrayList<SimpleObstacle> simpleObs) {
		if(connectionLines != null){
			return connectionLines;
		} else {
			connectionLines = new ArrayList<Line2>();
			for(SimpleObstacle so : simpleObs){
				if(!this.equals(so)){
					Line2 connectionLine = this.getShortestLineTo(so);
					if(connectionLine != null){
						connectionLines.add(connectionLine);
					}
				}
			}
			return connectionLines;
		}
	}
}
